package com.iwhalecloud.citybrain.flow.platform.manager.application.service.impl.task;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.gts.bpaas.middleware.mq.api.model.MqPayload;
import com.aliyun.gts.bpaas.middleware.mq.api.model.Transferable;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.context.MqMessageContext;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.context.ProcessItem;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.context.TaskWorkItem;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.enums.MqProcessTypeEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.application.config.MqRunnerTask;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.CallbackThirdPlatformAfterTaskCreatedService;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.ProcessCompletedPostProcessor;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.TaskCreatedPostProcessor;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.impl.MqProducer;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.BeanCopierUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.common.ConstanceVariable;
import com.iwhalecloud.citybrain.flow.platform.manager.common.TaskCreatedEventMsg;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.ModelCustomConfig;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.ParticipantVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowReModelCustomConfigRepository;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.flowable.variable.api.delegate.VariableScope;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

/**
 * @author lilh
 * @date 2021/4/23 14:13
 */
/*@Service
@Slf4j*/
@Slf4j
public class SendMqMessagePostProcessor implements TaskCreatedPostProcessor, ProcessCompletedPostProcessor {

    private final CallbackThirdPlatformAfterTaskCreatedService taskResolver;

    private final MqProducer producer;

    private final ThreadPoolTaskExecutor taskExecutor;

    private final FlowReModelCustomConfigRepository modelCustomConfigRepository;

    public SendMqMessagePostProcessor(CallbackThirdPlatformAfterTaskCreatedService taskResolver, MqProducer producer, ThreadPoolTaskExecutor taskExecutor, FlowReModelCustomConfigRepository modelCustomConfigRepository) {
        this.taskResolver = taskResolver;
        this.producer = producer;
        this.taskExecutor = taskExecutor;
        this.modelCustomConfigRepository = modelCustomConfigRepository;
    }

    @Override
    public void process(TaskEntity taskEntity) {
        TaskCreatedEventMsg taskInfo = taskResolver.resolveTaskCreatedMsg(taskEntity);
        taskInfo.setTimestamp(System.currentTimeMillis());
        //解析处理人
        resolveParticipants(taskEntity, taskInfo);
        //解析抄送人
        resolveCopiers(taskEntity, taskInfo);
        taskInfo.setModelName(resolveModelName(taskEntity));
        taskInfo.setApplyTime(resolveApplyTime(taskEntity));
        taskInfo.setApplyUserId(resolveApplyUserId(taskEntity));
        taskInfo.setApplyUserName(resolveApplyUserName(taskEntity));
        TaskWorkItem workItem = new TaskWorkItem();
        workItem.setTask(taskInfo);

        MqMessageContext messageContext = MqMessageContext.builder().payload(resolvePayload(workItem))
                .tag(resolveAppCode(taskEntity))
                .messageKey(taskInfo.getWorkItemId())
                .properties(new HashMap<String, String>(8) {
                    private static final long serialVersionUID = 574215689334684495L;

                    {
                        put(ConstanceVariable.MODEL_NAME, taskEntity.getVariable(ConstanceVariable.MODEL_NAME, String.class));
                    }
                })
                .build();
        //这里可以走异步，前面拼接参数还是得同步
        doSendMessage(messageContext);
    }

    @SneakyThrows
    private void resolveCopiers(TaskEntity taskEntity, TaskCreatedEventMsg taskInfo) {
        //可以搞两个线程来查询实例级别的抄送人信息和节点级别的抄送人信息
        List<TaskCreatedEventMsg.Participant> copiers = new ArrayList<>();
        taskInfo.setCopiers(copiers);
        String deploymentId = resolveDeploymentId(taskEntity);
        String nodeCode = taskEntity.getTaskDefinitionKey();
        //模型级别的抄送人配置
        Future<List<ModelCustomConfig>> modelLevelConfig = taskExecutor.submit(() -> modelCustomConfigRepository.listModelCustomConfig(deploymentId, null, ConstanceVariable.CC));
        //节点级别的抄送人配置
        Future<List<ModelCustomConfig>> nodeLevelConfig = taskExecutor.submit(() -> modelCustomConfigRepository.listModelCustomConfig(deploymentId, nodeCode, ConstanceVariable.CC));

        //其实每种级别最多只有一条配置
        covertConfigToParticipants(copiers, modelLevelConfig.get());

        //节点上最多也只有一条配置
        covertConfigToParticipants(copiers, nodeLevelConfig.get());

    }

    private void covertConfigToParticipants(List<TaskCreatedEventMsg.Participant> copiers, List<ModelCustomConfig> modelCustomCopierConfig) {
        ModelCustomConfig customConfig = null;
        if (CollectionUtil.isNotEmpty(modelCustomCopierConfig)
                && Objects.nonNull(customConfig = modelCustomCopierConfig.get(0))
                && StrUtil.isNotBlank(customConfig.getConfig())) {
            copiers.addAll(this.convert(customConfig.getConfig()));
        }
    }

    private List<TaskCreatedEventMsg.Participant> convert(String config) {
        List<CopierConfig> copierConfigs = JSONObject.parseArray(config, CopierConfig.class);
        return copierConfigs.stream().map(item -> {
            TaskCreatedEventMsg.Participant participant = new TaskCreatedEventMsg.Participant();
            participant.setParticipantId(item.getCode());
            participant.setParticipantName(item.getName());
            participant.setParticipantType("USR");
            return participant;
        }).collect(Collectors.toList());
    }

    private String resolveApplyUserName(TaskEntity taskEntity) {
        return taskEntity.getVariable(ConstanceVariable.APPLY_USER_NAME, String.class);
    }

    private String resolveApplyUserId(TaskEntity taskEntity) {
        return taskEntity.getVariable(ConstanceVariable.APPLY_USER_ID, String.class);
    }

    private Long resolveApplyTime(TaskEntity taskEntity) {
        return taskEntity.getVariable(ConstanceVariable.APPLY_TIME_STAMP, Long.class);
    }

    private String resolveDeploymentId(TaskEntity taskEntity) {
        return taskEntity.getVariable(ConstanceVariable.DEPLOYMENT_ID, String.class);
    }

    private String resolveModelName(TaskEntity taskEntity) {
        return taskEntity.getVariable(ConstanceVariable.MODEL_NAME, String.class);
    }

    private MqPayload<? extends Transferable> resolvePayload(TaskWorkItem workItem) {
        MqPayload<TaskWorkItem> payload = MqPayload.make(workItem);
        //payload.setUuid(workItem.getTask().getWorkItemId());
        if (Objects.nonNull(workItem.getTask())) {
            payload.setMessageKey(workItem.getTask().getWorkItemId());
        } else {
            if (Objects.nonNull(workItem.getProcessInstanceId())) {
                payload.setMessageKey(workItem.getProcessInstanceId());
            }
        }
        return payload;
    }

    private String resolveAppCode(VariableScope taskEntity) {
        return taskEntity.getVariable(ConstanceVariable.APP_CODE, String.class);
    }

    private void resolveParticipants(TaskEntity taskEntity, TaskCreatedEventMsg taskInfo) {
        List<ParticipantVo> participantVos = taskResolver.resolveParticipants(taskEntity);
        List<TaskCreatedEventMsg.Participant> participants = participantVos.stream().map(item -> {
            TaskCreatedEventMsg.Participant participant = new TaskCreatedEventMsg.Participant();
            BeanCopierUtils.copyProperties(item, participant);
            return participant;
        }).collect(Collectors.toList());
        taskInfo.setParticipants(participants);
    }

    @Override
    public void process(ExecutionEntity executionEntity) {
//        TaskWorkItem messageItem = new TaskWorkItem();
//        messageItem.setType(MqMessageTypeEnum.FINISH.getType());
//        messageItem.setProcessInstanceId(executionEntity.getProcessInstanceId());
//        MqMessageContext messageContext = MqMessageContext.builder().payload(resolvePayload(messageItem))
//                .tag(resolveAppCode(executionEntity))
//                .messageKey(messageItem.getProcessInstanceId())
//                .properties(new HashMap<String, String>(8) {
//
//                    private static final long serialVersionUID = -514556511973159032L;
//
//                    {
//                        put(ConstanceVariable.MODEL_NAME, executionEntity.getVariable(ConstanceVariable.MODEL_NAME, String.class));
//                    }
//                })
//                .build();
//        doSendMessage(messageContext);
        ProcessItem processItem = new ProcessItem();
        processItem.setProcessInstanceId(executionEntity.getProcessInstanceId());//流程实例id
        processItem.setProcessInstanceName(executionEntity.getVariable(ConstanceVariable.MODEL_NAME, String.class));
        processItem.setApplyUserName(executionEntity.getVariable(ConstanceVariable.APPLY_USER_NAME, String.class));
        processItem.setApplyUserId(executionEntity.getVariable(ConstanceVariable.APPLY_USER_ID, String.class));
        processItem.setProcessInstanceKey(executionEntity.getVariable(ConstanceVariable.MODEL_KEY, String.class));
        try {
            MqMessageContext messageContext = MqMessageContext.builder().payload(MqPayload.make(processItem))
                    .tag(MqProcessTypeEnum.FINISH.getType())
                    .messageKey(executionEntity.getProcessInstanceId())
                    .build();
            //这里可以走异步，前面拼接参数还是得同步
            doSendMessage(messageContext);
        } catch (Throwable e) {
            log.error("build messageContext error:", e);
        }
    }

    private void doSendMessage(MqMessageContext messageContext) {
        taskExecutor.execute(new MqRunnerTask(producer,messageContext));
    }

    @Data
    static class CopierConfig {

        private String code;

        private String name;

        private String type;
    }

}
